/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xos_http.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../msg_notify/head.h"
#include "../config/head.h"
#include "../macro/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "http_proc.h"
#include "fast_proc.h"

namespace xos_http
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    fast_proc::fast_proc()
    {
    }

    fast_proc::~fast_proc()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int fast_proc::proc( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        int nState = (int)pTask->m_eState;

        switch( nState )
        {
            case task::STATE_FILTER_BEFORE:
            {
                helper_before( pMsg );
            }
            break;
            case task::STATE_FILTER_AFTER:
            {
                helper_after( pMsg );
            }
            break;
            case task::STATE_DOWNLOAD_FILE:
            {
                helper_download_file( pMsg );
            }
            break;
            case task::STATE_PROC_TASK:
            {
                helper_proc( pMsg );
            }
            break;
            case task::STATE_PROC_XOS:
            {
                helper_xos( pMsg );
            }
            break;
            case task::STATE_DONE:
            {
                helper_done( pMsg );
            }
            break;
            default:
            {
            }
            break;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int fast_proc::get_servlet( task * pTask, const std::string & uri )
    {
        int ret = 0;

        http::i_servlet * pS = 0;
        file * pFile = 0;
        module * pM = 0;

        if( pTask->m_pPath->m_path != config::get()->m_filter )
        {
            xos_box::i_string * pStr = mgr::container()->box()->string();
            pStr->set( pTask->m_pRoot->m_full_uri.c_str() );
            pStr->url_append( uri.c_str() );
            pFile = mgr::get()->get_file_or_add_or_create_if_not_find( pStr->get( 0, 0 ), 0, false );
            xos_stl::release_interface( pStr );
        }

        if( pFile )
        {
            xos_box::i_str_property * pParams = pTask->values();
            const char * lpszServlet = pParams->str( config::get()->m_servlet.c_str() );
            pM = pFile->get_module();
            if( lpszServlet )
            {
                pS = pM->m_pServletMgr->create( lpszServlet );
            }
        }

        if( pS )
        {
            pTask->m_pServlet = pS;
            pTask->m_pModule = pM;
            pS = 0;
            pM = 0;
        }
        else
        {
            ret = 1;
        }

        xos_stl::release_interface( pS );
        xos_stl::release_ref( pM );

        return ret;
    }

    // 
    //    返回值：
    // 0 ：载入文件成功，进行后续处理
    // 1 : 处理失败，没有处理
    //-1 : 跳转任务，可能：1.异步xos；2.下载文件
    // 
    int fast_proc::proc_uri( task * pTask, const std::string & uri )
    {
        int ret = 0;

        xos_box::i_string * pStr = mgr::container()->box()->string();
        const char * lpszExt = 0;
        {
            lpszExt = mgr::container()->common()->misc()->ext( uri.c_str(), pStr );
            if( lpszExt )
            {
                pTask->m_name = pStr->get( 0, 0 );
                pTask->m_ext = lpszExt;
            }
            LOG4( "-------uri = %s", uri.c_str() );
        }

        if( lpszExt && (config::get()->m_xos == lpszExt ))
        {
            ret = get_servlet( pTask, uri );
            if( 0 == ret )
            {
                pTask->m_eState = task::STATE_PROC_XOS;
                ret = -1;
            }
            else
            {
                ret = 1;
            }
        }
        else
        {
            ret = check_file( pTask, uri );
            if( 0 == ret )
            {
                pTask->m_eState = task::STATE_DOWNLOAD_FILE;
                ret = -1;
            }
            else
            {
                ret = 1;
            }
        }

        xos_stl::release_interface( pStr );

        return ret;
    }

    // 
    // 0 : find this file
    // 1 : not find
    // 
    int fast_proc::check_file( task * pTask, const std::string & uri )
    {
        int ret = 0;
        xos_box::i_string * pStr = mgr::container()->box()->string();
        bool bFind = false;
        if( !pTask->m_ext.empty() )
        {
            const std::string & str = pTask->m_pRoot->m_full_path;
            pStr->set( str.c_str(), str.length() );
            pStr->url_append( pTask->m_name.c_str() );
            pStr->append( ".gz" );
            pTask->m_full_download_gz_file = pStr->get( 0, 0 );
        }
        {
            const std::string & str = pTask->m_pRoot->m_full_path;
            pStr->set( str.c_str(), str.length() );
            pStr->url_append( uri.c_str() );
            pTask->m_full_download_file = pStr->get( 0, 0 );
        }
        {
            const std::string & str = pTask->m_pRoot->m_full_uri;
            pStr->set( str.c_str(), str.length() );
            pStr->url_append( uri.c_str(), uri.length() );
            pTask->m_full_uri = pStr->get( 0, 0 );
        }
        if( !bFind )
        {
            file * pFile = mgr::get()->get_file_or_add_or_create_if_not_find( pTask->m_full_uri.c_str(), 0, false );
            if( pFile )
            {
                pTask->m_pDownloadFile = pFile;
                bFind = true;
            }
        }
        if( !bFind )
        {
            xos::i_dir * pDir = mgr::container()->xos()->dir();
            int r = pDir->is_file_exist( pTask->m_full_download_file.c_str() );
            if( 0 == r )
            {
                bFind = true;
            }
            xos_stl::release_interface( pDir );
        }
        xos_stl::release_interface( pStr );
        if( !bFind )
        {
            ret = 1;
        }
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int fast_proc::helper_download_file( xos_box::i_msg *& pMsg )
    {
        int ret = 0;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        bool bDone = false;
        file * pMyFile = 0;
        file * pFile = 0;
        // 先看看全局表中是否已经有文件了
        // m_pLockNum->set(1)是为了锁住抢到的任务
        // 别人看到为1，就知道已经被人抢走了
        // 别人看到为0，表示任务已经准备好了
        {
            xos_stl::init_pool_item( pMyFile );
            pMyFile->m_pLockNum->set(1);
            pFile = mgr::get()->get_file_or_add_or_create_if_not_find( pTask->m_full_uri.c_str(), &pMyFile, true );
            if( pMyFile != pFile )
            {
                xos_stl::release_interface( pMyFile );
            }
        }
        // 我没抢到任务
        if( !pMyFile )
        {
            pTask->m_pDownloadFile = pFile;
            int r = i_did_not_get_file_task( pTask );
            if( 0 == r )
            {
                bDone = true;
            }
        }
        // 我抢到了任务，进行文件读取，并判断是否需要压缩；
        // 如果需要，就压缩并缓存在本地
        // 文件一定存在，因为查过
        else
        {
            pTask->m_pDownloadFile = pMyFile;
            int r = i_get_file_task( pTask );
            if( 0 == r )
            {
                bDone = true;
            }
        }
        // 如果直接执行完成，就进行下一步
        if( bDone )
        {
            pMsg->set_int( 0, http::i_servlet::RET_DONE );
            pTask->m_eState = task::STATE_DONE;
            pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );
        }
        return ret;
    }

    int fast_proc::i_did_not_get_file_task( task * pTask )
    {
        int ret = 0;

        response * pResponse = pTask->m_pResponse;
        file * pFile = pTask->m_pDownloadFile;

        LOG4( "--------------------- i_did_not_get_file_task" );

        if( 0 == ret )
        {
            int r = pFile->add_task( pTask );
            // 成功加入等待通知列表，什么也不用做了
            if( 0 == r )
            {
                LOG4( "--------------------- i_did_not_get_file_task, add task success, i will lead and wait for other notify me when ready" );
                ret = 1;
            }
            else
            {
                LOG4( "--------------------- i_did_not_get_file_task, add task failed, other has prepared this file" );
            }
        }

        // 说明别人早已经处理完了，读取文件下载就行了
        // 如果有gz文件，helper_download会自动读取
        if( 0 == ret )
        {
            LOG4( "--------------------- i_did_not_get_file_task, othes have done, i begin read file" );
            http_proc obj;
            obj.helper_download( pResponse );
        }

        return ret;
    }

    int fast_proc::i_get_file_task( task * pTask )
    {
        int ret = 0;

        xos::i_dir * pDir = mgr::container()->xos()->dir();
        file * pFile = pTask->m_pDownloadFile;

        response * pResponse = pTask->m_pResponse;
        bool bNeedCompress = false;
        bool bDone = false;
        int nSize = 0;

        LOG4( "--------------------- i get the task and begin to read file" );

        if( !pTask->m_full_download_gz_file.empty() )
        {
            int r = pDir->is_file_exist( pTask->m_full_download_gz_file.c_str() );
            if( 0 == r )
            {
                LOG4( "--------------------- i get the task and the gz file exist. i do not need compress" );
                pFile->m_full_gz_file = pTask->m_full_download_gz_file;
                bDone = true;
            }
        }

        if( !pTask->m_full_download_file.empty() )
        {
            pFile->m_full_file = pTask->m_full_download_file;
        }

        // 读取文件
        {
            http_proc obj;
            nSize = obj.helper_download( pResponse );
        }

        if( !bDone && !pTask->m_ext.empty() )
        {
            bNeedCompress = mgr::container()->http()->if_need_compress_by_ext( pTask->m_ext.c_str(), nSize );
        }

        if( bNeedCompress )
        {
            pResponse->tags()->set( xos_http_protocol::HTTP_CONTENT_ENCODING, xos_http_protocol::HTTP_ZIP_DEFLATE );
            xos_box::i_list * pList = mgr::container()->box()->list();
            xos_box::i_list * pBodyList = pResponse->body_list();
            xos_compress::i_compress * pZlib = mgr::container()->compress()->zlib();
            pZlib->compress( pList, pBodyList );
            pBodyList->swap( pList );
            xos_stl::release_interface( pZlib );
            xos_stl::release_interface( pList );
        }

        // 存到磁盘
        if( bNeedCompress )
        {
            xos_box::i_list * pBodyList = pResponse->body_list();
            xos::i_file * pF = mgr::container()->xos()->file();
            int r = pF->open( pTask->m_full_download_gz_file.c_str(), xos::i_file::XOS_FILE_WRITE, xos::i_file::XOS_SHARE_READ, xos::i_file::XOS_FILE_CREATE );
            if( 0 == r )
            {
                pF->write( pBodyList );
            }
            pFile->m_full_gz_file = pTask->m_full_download_gz_file;
            xos_stl::release_interface( pF );
        }

        // 通知挂住的下载文件任务
        pFile->notify_block_task();

        xos_stl::release_interface( pDir );

        return ret;
    }

    // before过滤器可以通过返回i_servlet::RET_FILTER_FAST_RETURN来过滤掉后边的before过滤器和任务处理，跳到after过滤器处
    // 所以如果想在过滤器中跳出来，就要准备好返回给浏览器的数据
    int fast_proc::helper_before( xos_box::i_msg *& pMsg )
    {
        int ret = 0;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        pTask->m_eState = task::STATE_FILTER_AFTER;
        {
            http_proc obj;
            ret = obj.filter( pTask, config::get()->m_before );
        }
        if( 0 == ret )
        {
            pTask->m_eState = task::STATE_PROC_TASK;
        }
        pTask->get_connect()->fast_notify()->notify( pMsg );
        return ret;
    }

    // after过滤器可以通过返回i_servlet::RET_FILTER_FAST_RETURN过滤掉后边的after过滤器，开始组装http协议
    int fast_proc::helper_after( xos_box::i_msg *& pMsg )
    {
        int ret = 0;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        int nMsg = FAST_COMPOSE_RETURN;
        {
            http_proc obj;
            obj.filter( pTask, config::get()->m_after );
        }
        pTask->get_connect()->fast_notify()->notify( pMsg, nMsg );
        return ret;
    }

    // 
    // 只有ret == 0时，才需要修改task的状态为STATE_DONE
    // 
    // ret == 0，说明是所有尝试都失败了，需要返回404，本次请求结束了
    // ret == 1，说明本次尝试失败，需要进行下一个文件的尝试。
    // ret ==-1，说明要跳转，可能:1.切到慢线程；2.下载文件
    // 
    int fast_proc::helper_proc( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        http::i_servlet::enumRet eRet = http::i_servlet::RET_DONE;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );

        if( pTask->m_uri_list_iter != pTask->m_uri_list.end() )
        {
            std::string & uri = *pTask->m_uri_list_iter++;
            ret = proc_uri( pTask, uri );
        }
        else
        {
            http_proc obj;
            obj.helper_404( pTask->m_pResponse );
        }

        if( 0 == ret )
        {
            pTask->m_eState = task::STATE_DONE;
            pMsg->set_int( 0, eRet );
        }

        pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );

        return ret;
    }

    int fast_proc::helper_xos( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        http::i_servlet::enumRet eRet = http::i_servlet::RET_DONE;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        http::i_servlet * pServlet = pTask->m_pServlet;

        {
            eRet = pServlet->proc( pTask );
        }

        if( http::i_servlet::RET_CALL != eRet )
        {
            pTask->m_eState = task::STATE_DONE;
            pMsg->set_int( 0, eRet );
            pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );
        }

        return ret;
    }

    int fast_proc::helper_done( xos_box::i_msg *& pMsg )
    {
        int ret = 0;
        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        int nRet = pMsg->get_int( 0, 0 );
        switch( nRet )
        {
            // 想要返回快速线程，那一定是servelet中的慢速异步任务完成了
            case http::i_servlet::RET_FAST:
            {
                pTask->m_eState = task::STATE_PROC_XOS;
                pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );
            }
            break;
            // 进行慢速异步任务
            case http::i_servlet::RET_SLOW:
            {
                pTask->m_eState = task::STATE_PROC_TASK;
                pTask->get_connect()->slow_notify()->notify( pMsg, SLOW_JOB );
            }
            break;
            // 快速任务完成，或者task::call返回
            // 到这返栈，如果有的话
            case http::i_servlet::RET_DONE:
            {
                servlet_done( pMsg );
            }
            break;
            default:
            {
            }
            break;
        }
        return ret;
    }

    int fast_proc::servlet_done( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );

        // 释放本次调用的模块，如果有的话。
        {
            http::i_servlet *& pS = pTask->m_pServlet;
            module *& pM = pTask->m_pModule;
            xos_stl::release_interface( pS );
            xos_stl::release_ref( pM );
        }

        // 恢复环境，如果有的话
        // 如果没有，那就开始下半部分的过滤器
        if( 0 != pTask->pop() )
        {
            pTask->m_eState = task::STATE_FILTER_AFTER;
        }

        pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );

        return ret;
    }

} // xos_http
